# @Version : 1.0
# @Author  : wxz
# @File    : app_logger.py
# @Time    : 2025/6/14 10:34
# @Describe: 描述...
import logging
from logging.handlers import RotatingFileHandler
import colorlog
from datetime import datetime
import os
from typing import Optional, Callable
from http.client import HTTPConnection
from fastapi import Request
from fastapi.logger import logger as fastapi_logger

from config import app_settings


class AppLogger:
    def __init__(self, name: str, log_dir: str = 'logs', max_bytes: int = 1024 * 1024,
                 backup_count: int = 6, level: int = logging.INFO):
        """
        初始化增强版日志记录器，支持SQLAlchemy和HTTP请求日志

        参数:
            name: 应用名称
            log_dir: 日志目录
            max_bytes: 日志文件最大大小
            backup_count: 备份文件数量
            level: 日志级别
        """
        self.name = name
        self.base_log_dir = log_dir
        self.max_bytes = max_bytes
        self.backup_count = backup_count

        # 创建日志目录
        self.current_log_dir = self._get_current_log_dir()
        os.makedirs(self.current_log_dir, exist_ok=True)

        # 创建日志记录器
        self.logger = logging.getLogger(name)
        self.logger.setLevel(level)

        # 清除已有处理器
        self._clear_handlers()

        # 设置处理器
        self._setup_handlers()

        # 禁止传递给父logger
        self.logger.propagate = False

        # 配置SQLAlchemy日志
        self._setup_sqlalchemy_logging()

        # 配置HTTP请求日志
        self._setup_http_logging()

        # 配置FastAPI日志
        self._setup_fastapi_logging()

    def _get_current_log_dir(self) -> str:
        """获取当前日志目录（按小时创建）"""
        timestamp = datetime.now().strftime("%Y-%m-%d-%H")
        return os.path.join(self.base_log_dir, timestamp)

    def _get_timestamp(self) -> str:
        """获取当前时间戳（格式：YYYY-MM-DD-HH）"""
        return datetime.now().strftime("%Y-%m-%d-%H")

    def _generate_filename(self, log_type: str, suffix: Optional[str] = None) -> str:
        """
        生成日志文件名
        格式: {type}-{timestamp}-{name}.log[{.suffix}]
        示例: info-2025-01-01-12-my_app.log.1
        """
        timestamp = self._get_timestamp()
        filename = f"{log_type}-{timestamp}-{self.name}.log"
        if suffix:
            filename += f".{suffix}"
        return os.path.join(self.current_log_dir, filename)

    def _clear_handlers(self):
        """清除已有的日志处理器"""
        for handler in self.logger.handlers[:]:
            self.logger.removeHandler(handler)
            handler.close()

    def _setup_handlers(self):
        """配置所有日志处理器"""
        # 设置info日志处理器（只记录INFO级别）
        self._setup_file_handler("info", logging.INFO)

        # 设置error日志处理器（记录WARNING及以上级别）
        self._setup_file_handler("error", logging.WARNING)

        # 设置控制台处理器（带颜色输出）
        self._setup_console_handler()

    def _setup_file_handler(self, log_type: str, level: int):
        """配置文件日志处理器"""
        current_log = self._generate_filename(log_type)

        # 自定义滚动命名函数
        def namer(default_name: str) -> str:
            # 提取序号（如app.log.1中的1）
            parts = default_name.split('.')
            suffix = parts[-1] if len(parts) > 2 else '1'
            return self._generate_filename(log_type, suffix)

        # 自定义滚动函数
        def rotator(source: str, dest: str):
            # 删除最旧的备份文件（如果存在）
            oldest = self._generate_filename(log_type, str(self.backup_count))
            if os.path.exists(oldest):
                os.remove(oldest)

            # 重命名现有备份文件（如.log.2 -> .log.3）
            for i in range(self.backup_count - 1, 0, -1):
                src = self._generate_filename(log_type, str(i))
                if os.path.exists(src):
                    os.rename(src, self._generate_filename(log_type, str(i + 1)))

            # 重命名当前文件为.log.1
            os.rename(source, self._generate_filename(log_type, '1'))

        handler = RotatingFileHandler(
            filename=current_log,
            maxBytes=self.max_bytes,
            backupCount=self.backup_count,
            encoding='utf-8'
        )
        handler.namer = namer
        handler.rotator = rotator
        handler.setLevel(level)

        # 设置过滤器
        if log_type == "info":
            handler.addFilter(lambda record: record.levelno == logging.INFO)

        # 设置日志格式
        formatter = logging.Formatter(
            '%(asctime)s [%(levelname)-8s] %(name)s: %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S'
        )
        handler.setFormatter(formatter)

        self.logger.addHandler(handler)

    def _setup_console_handler(self):
        """配置控制台日志处理器（带颜色输出）"""
        handler = logging.StreamHandler()
        handler.setLevel(logging.DEBUG)

        formatter = colorlog.ColoredFormatter(
            '%(log_color)s%(asctime)s [%(levelname)-8s] %(name)s: %(message)s',
            datefmt='%Y-%m-%d %H:%M:%S',
            log_colors={
                'DEBUG': 'cyan',
                'INFO': 'green',
                'WARNING': 'yellow',
                'ERROR': 'red',
                'CRITICAL': 'red,bg_white',
            },
            secondary_log_colors={}
        )
        handler.setFormatter(formatter)

        self.logger.addHandler(handler)

    def _setup_sqlalchemy_logging(self):
        """配置SQLAlchemy日志"""
        # 设置SQLAlchemy日志级别
        sqlalchemy_logger = logging.getLogger('sqlalchemy')
        sqlalchemy_logger.setLevel(logging.INFO)

        # 使用相同的文件和控制台处理器
        for handler in self.logger.handlers:
            sqlalchemy_logger.addHandler(handler)

        # 设置引擎日志
        sqlalchemy_logger = logging.getLogger('sqlalchemy.engine')
        sqlalchemy_logger.setLevel(logging.INFO)

    def _setup_http_logging(self):
        """配置HTTP请求日志"""
        # 启用HTTPConnection调试
        HTTPConnection.debuglevel = 1

        # 获取HTTP请求日志记录器
        http_logger = logging.getLogger('http.client')
        http_logger.setLevel(logging.DEBUG)

        # 使用相同的文件和控制台处理器
        for handler in self.logger.handlers:
            http_logger.addHandler(handler)

    def _setup_fastapi_logging(self):
        """配置FastAPI请求日志"""
        # 重定向FastAPI默认日志到我们的日志系统
        fastapi_logger.handlers = self.logger.handlers
        fastapi_logger.setLevel(self.logger.level)
        fastapi_logger.propagate = False

    def get_fastapi_middleware(self) -> Callable:
        """获取FastAPI请求日志中间件"""

        async def log_middleware(request: Request, call_next):
            # 记录请求开始
            self.logger.info(f"Request started: {request.method} {request.url.path}")

            try:
                response = await call_next(request)

                # 记录请求完成
                self.logger.info(
                    f"Request completed: {request.method} {request.url.path} "
                    f"Status: {response.status_code}"
                )
                return response
            except Exception as e:
                # 记录请求异常
                self.logger.error(
                    f"Request failed: {request.method} {request.url.path} "
                    f"Error: {str(e)}", exc_info=True
                )
                raise

        return log_middleware

    def _setup_uvicorn_logging(self):
        """配置Uvicorn日志"""
        # 获取Uvicorn相关日志记录器
        uvicorn_loggers = [
            "uvicorn",
            "uvicorn.error",
            "uvicorn.access",
            "uvicorn.asgi"
        ]

        for logger_name in uvicorn_loggers:
            uv_logger = logging.getLogger(logger_name)
            uv_logger.handlers = self.logger.handlers
            uv_logger.setLevel(self.logger.level)
            uv_logger.propagate = False

    # 日志记录方法
    def debug(self, msg: str, *args, **kwargs):
        self.logger.debug(msg, *args, **kwargs)

    def info(self, msg: str, *args, **kwargs):
        self.logger.info(msg, *args, **kwargs)

    def warning(self, msg: str, *args, **kwargs):
        self.logger.warning(msg, *args, **kwargs)

    def error(self, msg: str, *args, **kwargs):
        self.logger.error(msg, *args, **kwargs)

    def critical(self, msg: str, *args, **kwargs):
        self.logger.critical(msg, *args, **kwargs)

    def exception(self, msg: str, *args, **kwargs):
        self.logger.exception(msg, *args, **kwargs)


# 创建日志记录器
logger = AppLogger(
    name=app_settings.log.name,
    log_dir=app_settings.log.log_dir,  # 日志根目录
    max_bytes=app_settings.log.max_bytes,  # 1M
    backup_count=app_settings.log.backup_count,  # 保留6个备份
    level=app_settings.log.level  # 日志级别
)

# 使用示例
if __name__ == "__main__":
    # 创建日志记录器
    logger = AppLogger(
        name='my_app',
        log_dir='./logs',  # 日志根目录
        max_bytes=1024 * 500,  # 500KB（便于测试）
        backup_count=100,  # 保留6个备份
        level=logging.DEBUG  # 日志级别
    )

    # 测试不同级别日志
    logger.debug("这是一条调试信息（只显示在控制台）")
    logger.info("这是一条普通信息（记录到info日志和控制台）")
    logger.warning("这是一条警告信息（记录到error日志和控制台）")
    logger.error("这是一条错误信息（记录到error日志和控制台）")
    logger.critical("这是一条严重错误信息（记录到error日志和控制台）")

    # try:
    #     1 / 0
    # except Exception as e:
    #     logger.exception("发生异常（记录到error日志和控制台）: %s", e)

    # 测试日志滚动（写入足够内容使文件超过设定大小）
    # print("\n开始测试日志滚动...")
    # for i in range(10000):
    #     logger.info(f"测试info日志滚动 - 消息 {i}: " + "这是一条足够长的日志消息，用于测试日志文件滚动功能。" * 10)
    #     #logger.error(f"测试error日志滚动 - 消息 {i}: " + "这是一条足够长的错误日志消息，用于测试日志文件滚动功能。" * 10)
    #
    # print("测试完成，请检查logs目录下的文件结构")

